"""
@File: auth.py
@Project: 约个球
@Author: zhangjian
@Time: 2022.8.27 16:21
@Description: None
"""
from flask import request, current_app
from libs.response import generate_response
from functools import wraps
from hashlib import md5
from model.user import ApiToken
import time
from itsdangerous import TimedJSONWebSignatureSerializer as TJS  # 给模块设置别名
from itsdangerous import BadSignature, SignatureExpired
from libs.error_code import TokenFailException


def login(func):
    """自定义权限认证装饰器(闭包)"""
    @wraps(func)  # 保留装饰的函数元数据
    def inner(*args, **kwargs):
        token = request.args.get("token")
        if api_auth():
            uid = verify_token(token)
            print(f"token is {uid}")
            result = func(*args, **kwargs)
            return result
        elif not token and api_auth():
            result = func(*args, **kwargs)
            return result
        else:
            return generate_response(status_code=10002, message="auth fail")
    return inner


def api_auth():
    params = request.args  # 客户端url传递过来的参数
    appid = params.get("appid")  # 应用id
    salt = params.get("salt")  # 盐值
    sign = params.get("sign")  # 签名
    timestamp = params.get("timestamp")  # 时间戳

    if time.time() - float(timestamp) > 600:
        return False

    api_token = ApiToken.query.filter_by(appid=appid).first()
    if not api_token:
        return False

    # 验证有没有此url和方法的权限
    if not has_permission(api_token, request.path, request.method.lower()):
        return False

    # 获取数据库里的密钥
    secretkey = api_token.secretkey
    # 生成服务端的签名
    # 可以加上时间戳来防止签名被别人盗取，重复访问
    user_sign = appid + salt + secretkey + timestamp
    m1 = md5()
    m1.update(user_sign.encode(encoding="utf-8"))
    # 判断客户端传递过来的签名和服务端生成签名是否一致
    if sign != m1.hexdigest():
        # raise AuthFailException
        return False
    else:
        return True


def has_permission(api_token, url, method):
    """url验证"""
    # 客户端请求的方法和url
    mypermission = method + url
    # 获取此api_token对象的所有url权限，如['get/v1/monitor', 'post/v1/monitor']
    all_permission = [permission.method_type + permission.url
                      for permission in api_token.manage]
    if mypermission in all_permission:
        return True
    else:
        return False


def verify_token(token):
    """签名认证  -- api授权
    客户端向服务管理人员请求appid和secretkey
    服务端提供appid和secretkey，以及相应的算法，客户端拿到信息之后根据相应的算法生成签名，发送给服务端
    服务端比较签名是否一致
    安全、时间戳、https"""
    s = TJS(current_app.config["SECRET_KEY"])
    try:
        data = s.loads(token)
    except BadSignature:
        raise TokenFailException
    except SignatureExpired:
        raise RuntimeError("token过期")
    return data


def create_token(uid):
    """eyJhbGciOiJIUzUxMiIsImlhdCI6MTY2MTMwNzU1OCwiZXhwIjoxNjYxMzA3NjE4fQ.eyJ1aWQiOjE1fQ.8qVjKIWhyQb_mvjRWb5x5sTLC3dln \
    lK8lOUXHX0rBi7Ua5sg51PPv5r_XM6QXZZYxxV3Bl5ooq0_JXyY09AOpA
    token规范：https://www.jianshu.com/p/50ade6f2e4fd
    三个内容，每个内容之间用点分割header.palyload.signature
    header:base64({alg:hs512,iat:164545,exp:168453})
    palyload:base64enc(uid:15)
    signature:sha512(base64enc(header)+.+base64enc(playload)+serectkey)"""
    #第一个参数传递内部私钥，测试环境写死，第二个参数是有效期
    s = TJS(current_app.config["SECRET_KEY"], expires_in=current_app.config["EXPIRES_IN"])
    token = s.dumps({"uid":uid}).decode("ascii")
    return token